package nsfs

import (
	"GLibHac/fssystem"
	"GLibHac/tools"
	"fmt"
	"math"
)

type Validity byte

const (
	ValidityUnchecked = Validity(iota)
	ValidityInvalid
	ValidityValid
	ValidityMissingKey
)

func (i Validity) String() string {
	switch i {
	case ValidityUnchecked:
		return "Unchecked"
	case ValidityInvalid:
		return "Invalid"
	case ValidityValid:
		return "Valid"
	case ValidityMissingKey:
		return "MissingKey"
	default:
		return fmt.Sprintf("Validity(%d)", i)
	}
}

type HierarchicalIntegrityVerificationStorage struct {
	fssystem.IStorageAbstract
	levels              []fssystem.IStorage
	integrityCheckLevel IntegrityCheckLevel
	levelValidities     [][]Validity
	integrityStorages   []*IntegrityVerificationStorage
	dataLevel           fssystem.IStorage
	length              int64
}

func (i *HierarchicalIntegrityVerificationStorage) Size() int64 {
	return i.length
}

func NewHierarchicalIntegrityVerificationStorage(levelInfo []*IntegrityVerificationInfo, integrityCheckLevel IntegrityCheckLevel) (*HierarchicalIntegrityVerificationStorage, error) {
	storage := &HierarchicalIntegrityVerificationStorage{
		levels:              make([]fssystem.IStorage, len(levelInfo)),
		integrityCheckLevel: integrityCheckLevel,
		levelValidities:     make([][]Validity, len(levelInfo)-1),
		integrityStorages:   make([]*IntegrityVerificationStorage, len(levelInfo)-1),
	}
	storage.levels[0] = levelInfo[0].Data

	for i := 1; i < len(storage.levels); i++ {
		levelData := NewIntegrityVerificationStorage(levelInfo[i], storage.levels[i-1], integrityCheckLevel)
		levelSize := levelData.Size()

		cacheCount := int32(math.Min(float64(tools.DivideUp(levelSize, int64(levelInfo[i].BlockSize))), 4))
		storage.levels[i] = NewCachedStorage(levelData, levelData.SectorSize, cacheCount)
		storage.levelValidities[i-1] = levelData.BlockValidities
		storage.integrityStorages[i-1] = levelData
	}

	storage.dataLevel = storage.levels[len(storage.levels)-1]
	dataSize := storage.dataLevel.Size()
	storage.length = dataSize
	storage.SizeFunc = storage.Size
	storage.ReadFunc = storage.dataLevel.Read
	return storage, nil
}
